﻿using System;

namespace CommonFramework.FixMath
{
    [Serializable]
    public struct FVector2
    {
        private int _rawX;
        private int _rawY;

        public FFloat X
        {
            get => FFloat.FromRawInt(_rawX);
            set => _rawX = value.RawVal();
        }
        public FFloat Y
        {
            get => FFloat.FromRawInt(_rawY);
            set => _rawY = value.RawVal();
        }

        public static readonly FVector2 Zero = FVector2.FromRawInt(0, 0);
        public static readonly FVector2 One = FVector2.FromRawInt(FFloat.Precision, FFloat.Precision);
        public static readonly FVector2 Half = FVector2.FromRawInt(FFloat.Precision / 2, FFloat.Precision / 2);
        public static readonly FVector2 Up = FVector2.FromRawInt(0, FFloat.Precision);
        public static readonly FVector2 Down = FVector2.FromRawInt(0, -FFloat.Precision);
        public static readonly FVector2 Right = FVector2.FromRawInt(FFloat.Precision, 0);
        public static readonly FVector2 Left = FVector2.FromRawInt(-FFloat.Precision, 0);

        private FVector2(int rawX, int rawY)
        {
            this._rawX = rawX;
            this._rawY = rawY;
        }
        public static FVector2 FromRawInt(int rawX, int rawY)
        {
            return new FVector2(rawX, rawY);
        }
        public static FVector2 FromInt(int x, int y)
        {
            return new FVector2(x * FFloat.Precision, y * FFloat.Precision);
        }
        public static FVector2 FromFFloat(FFloat x, FFloat y)
        {
            return new FVector2(x.RawVal(), y.RawVal());
        }

        private static readonly int[] _rotations = new int[] {
            1, 0, 0,
            1, 0, 1,
            -1, 0, -1,
            0, 0, -1,
            0, -1, 1,
            0
        };

        /// <summary>
        /// 顺时针旋转90Deg 参数
        /// </summary>
        public const int ROTATE_CW_90 = 1;
        public const int ROTATE_CW_180 = 2;
        public const int ROTATE_CW_270 = 3;
        public const int ROTATE_CW_360 = 4;
        /// <summary>
        /// clockwise 顺时针旋转  
        /// 1表示顺时针旋转 90 degree
        /// 2表示顺时针旋转 180 degree
        /// </summary>
        public static FVector2 Rotate(FVector2 v, int r)
        {
            r %= 4;
            return FVector2.FromRawInt(
                v._rawX * FVector2._rotations[r * 4] + v._rawY * FVector2._rotations[r * 4 + 1],
                v._rawX * FVector2._rotations[r * 4 + 2] + v._rawY * FVector2._rotations[r * 4 + 3]);
        }

        public FVector2 Rotate(FFloat deg)
        {
            var rad = FMathUtil.Deg2Rad * deg;
            FFloat cos, sin;
            FMathUtil.SinCos(out sin, out cos, rad);
            return FVector2.FromFFloat(this.X * cos - this.Y * sin, this.X * sin + this.Y * cos);
        }

        public static FVector2 Min(FVector2 a, FVector2 b)
        {
            return FVector2.FromRawInt(FMathUtil.Min(a._rawX, b._rawX), FMathUtil.Min(a._rawY, b._rawY));
        }

        public static FVector2 Max(FVector2 a, FVector2 b)
        {
            return FVector2.FromRawInt(FMathUtil.Max(a._rawX, b._rawX), FMathUtil.Max(a._rawY, b._rawY));
        }

        public void Min(ref FVector2 r)
        {
            this._rawX = FMathUtil.Min(this._rawX, r._rawX);
            this._rawY = FMathUtil.Min(this._rawY, r._rawY);
        }

        public void Max(ref FVector2 r)
        {
            this._rawX = FMathUtil.Max(this._rawX, r._rawX);
            this._rawY = FMathUtil.Max(this._rawY, r._rawY);
        }


        public void Normalize()
        {
            long num = (long)(this._rawX * 100);
            long num2 = (long)(this._rawY * 100);
            long num3 = num * num + num2 * num2;
            if (num3 == 0L)
            {
                return;
            }

            long b = (long)FMathUtil.Sqrt(num3);
            this._rawX = (int)(num * 1000L / b);
            this._rawY = (int)(num2 * 1000L / b);
        }

        public FFloat SqrMagnitude
        {
            get
            {
                long num = (long)this._rawX;
                long num2 = (long)this._rawY;
                return FFloat.FromRawLong((num * num + num2 * num2) / FFloat.Precision);
            }
        }

        public long RawSqrMagnitude
        {
            get
            {
                long num = (long)this._rawX;
                long num2 = (long)this._rawY;
                return num * num + num2 * num2;
            }
        }

        public FFloat Magnitude
        {
            get
            {
                long num = (long)this._rawX;
                long num2 = (long)this._rawY;
                return FFloat.FromRawLong(FMathUtil.Sqrt(num * num + num2 * num2));
            }
        }

        public FVector2 Normalized
        {
            get
            {
                FVector2 result = FVector2.FromRawInt(this._rawX, this._rawY);
                result.Normalize();
                return result;
            }
        }

        public static FVector2 operator +(FVector2 a, FVector2 b)
        {
            return FVector2.FromRawInt(a._rawX + b._rawX, a._rawY + b._rawY);
        }

        public static FVector2 operator -(FVector2 a, FVector2 b)
        {
            return FVector2.FromRawInt(a._rawX - b._rawX, a._rawY - b._rawY);
        }

        public static FVector2 operator -(FVector2 lhs)
        {
            lhs._rawX = -lhs._rawX;
            lhs._rawY = -lhs._rawY;
            return lhs;
        }

        public static FVector2 operator *(FFloat rhs, FVector2 lhs)
        {
            lhs._rawX = (int)(((long)(lhs._rawX * rhs.RawVal())) / FFloat.Precision);
            lhs._rawY = (int)(((long)(lhs._rawY * rhs.RawVal())) / FFloat.Precision);
            return lhs;
        }

        public static FVector2 operator *(FVector2 lhs, FFloat rhs)
        {
            lhs._rawX = (int)(((long)(lhs._rawX * rhs.RawVal())) / FFloat.Precision);
            lhs._rawY = (int)(((long)(lhs._rawY * rhs.RawVal())) / FFloat.Precision);
            return lhs;
        }
        public static FVector2 operator *(int rhs, FVector2 lhs)
        {
            lhs._rawX = lhs._rawX * rhs;
            lhs._rawY = lhs._rawY * rhs;
            return lhs;
        }

        public static FVector2 operator *(FVector2 lhs, int rhs)
        {
            lhs._rawX = lhs._rawX * rhs;
            lhs._rawY = lhs._rawY * rhs;
            return lhs;
        }
        public static FVector2 operator /(FVector2 lhs, FFloat rhs)
        {
            lhs._rawX = (int)(((long)lhs._rawX * FFloat.Precision) / rhs.RawVal());
            lhs._rawY = (int)(((long)lhs._rawY * FFloat.Precision) / rhs.RawVal());
            return lhs;
        }
        public static FVector2 operator /(FVector2 lhs, int rhs)
        {
            lhs._rawX = lhs._rawX / rhs;
            lhs._rawY = lhs._rawY / rhs;
            return lhs;
        }
        public static bool operator ==(FVector2 a, FVector2 b)
        {
            return a._rawX == b._rawX && a._rawY == b._rawY;
        }

        public static bool operator !=(FVector2 a, FVector2 b)
        {
            return a._rawX != b._rawX || a._rawY != b._rawY;
        }

        public static implicit operator FVector3(FVector2 v)
        {
            return FVector3.FromRawInt(v._rawX, v._rawY, 0);
        }

        public override bool Equals(object o)
        {
            if (o == null)
            {
                return false;
            }

            FVector2 vInt = (FVector2)o;
            return this._rawX == vInt._rawX && this._rawY == vInt._rawY;
        }

        public override int GetHashCode()
        {
            return this._rawX * 49157 + this._rawY * 98317;
        }

        public override string ToString()
        {
            return string.Format("({0},{1})", _rawX * FFloat.PrecisionInverse, _rawY * FFloat.PrecisionInverse);
        }

        public FFloat this[int index]
        {
            get
            {
                switch (index)
                {
                    case 0: return X;
                    case 1: return Y;
                    default: throw new IndexOutOfRangeException("vector idx invalid" + index);
                }
            }

            set
            {
                switch (index)
                {
                    case 0:
                    _rawX = value.RawVal();
                    break;
                    case 1:
                    _rawY = value.RawVal();
                    break;
                    default: throw new IndexOutOfRangeException("vector idx invalid" + index);
                }
            }
        }


        public static FFloat Dot(FVector2 u, FVector2 v)
        {
            return FFloat.FromRawLong(((long) u._rawX * v._rawX + (long) u._rawY * v._rawY) / FFloat.Precision);
        }

        public static FFloat Cross(FVector2 a, FVector2 b)
        {
            return FFloat.FromRawLong(((long) a._rawX * (long) b._rawY - (long) a._rawY * (long) b._rawX) /
                                      FFloat.Precision);
        }

        public static FVector2 Lerp(FVector2 a, FVector2 b, FFloat f)
        {
            return FVector2.FromRawInt(
                (int)(((long)(b._rawX - a._rawX) * f.RawVal()) / FFloat.Precision) + a._rawX,
                (int)(((long)(b._rawY - a._rawY) * f.RawVal()) / FFloat.Precision) + a._rawY);
        }
    }
}